﻿using System;
using System.Collections.Generic;
using System.Linq;
using Metro.Kashikoi.Common.ExceptionHandling;
using Metro.Kashikoi.Service.Common.Entity;
using Metro.Kashikoi.Service.Item.Entity;
using Metro.Kashikoi.ServiceContract.Item;
using ItemBase = Metro.Kashikoi.ServiceContract.Item.Item;

namespace Metro.Kashikoi.Service.Item.Business
{
    /// <summary>
    /// This class implements the interface <c>IItemFacade</c>.
    /// </summary>
    class ItemFacade : IItemFacade
    {
        #region IItemFacade Members

        /// <summary>
        /// Deletes the items of vendor.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="vendorId">The vendor id.</param>
        /// <param name="deletedItems">The deleted items.</param>
        public void DeleteItemsOfVendor(IUnitOfWork context, int vendorId, IList<int> deletedItems)
        {
            var rep = new Repository<ItemBase>(context);

            foreach (var item in rep.Where<StockItem>(o => o.Vendor.Id == vendorId && o.Deleted == false))
            {
                item.Deleted = true;
                deletedItems.Add(item.Id);
            }
        }

        /// <summary>
        /// Deletes the items of category.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="categoryId">The category id.</param>
        /// <param name="deletedItems">The deleted items.</param>
        public void DeleteItemsOfCategory(IUnitOfWork context, int categoryId, IList<int> deletedItems)
        {
            var rep = new Repository<ItemBase>(context);

            foreach (var item in rep.Where<StockItem>(o => o.Category.Id == categoryId && o.Deleted == false))
            {
                item.Deleted = true;
                deletedItems.Add(item.Id);
            }
        }

        /// <summary>
        /// Creates the item.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="item">The item.</param>
        /// <param name="category">The category.</param>
        /// <param name="vendor">The vendor.</param>
        public void CreateItem(IUnitOfWork context, StockItem item, Category category, Vendor vendor)
        {
            var rep = new Repository<ItemBase>(context);

            CheckDuplicate(rep, item);
            item.Deleted = false;
            item.Category = category;
            item.Vendor = vendor;
            item.ChargeDepartment = GetChargeDepartment(context, item.ChargeDepartment.Id);
            rep.Add(item);
        }

        /// <summary>
        /// Updates the item.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="item">The item.</param>
        /// <param name="category">The category.</param>
        /// <param name="vendor">The vendor.</param>
        public void UpdateItem(IUnitOfWork context, StockItem item, Category category, Vendor vendor)
        {
            var rep = new Repository<ItemBase>(context);

            var entity = rep.GetItem(item.Id);
            if (entity.Name != item.Name)
            {
                CheckDuplicate(rep, item);
                entity.Name = item.Name;
            }
            if (entity.Category.Id != category.Id)
            {
                entity.Category = category;
            }
            if (entity.Vendor.Id != vendor.Id)
            {
                entity.Vendor = vendor;
            }
            entity.GenericName = item.GenericName;
            entity.BrandName = item.BrandName;
            entity.AlternatName = item.AlternatName;
            entity.VendorCode = item.VendorCode;
            entity.UnitCost = item.UnitCost;
            entity.ChargeCode = item.ChargeCode;
            entity.ChargeAmount = item.ChargeAmount;
            entity.PackSize = item.PackSize;
            var chargeDepartment = GetChargeDepartment(context, item.ChargeDepartment.Id);
            if (entity.ChargeDepartment.Id != chargeDepartment.Id)
            {
                entity.ChargeDepartment = chargeDepartment;
            }
        }

        /// <summary>
        /// Deletes the item.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="itemId">The item id.</param>
        public void DeleteItem(IUnitOfWork context, int itemId)
        {
            var rep = new Repository<ItemBase>(context);
            var entity = rep.GetItem(itemId);
            entity.Deleted = true;
        }

        /// <summary>
        /// Gets all items.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        /// A list of all itmes.
        /// </returns>
        public IList<StockItem> GetAllItems(IUnitOfWork context)
        {
            var rep = new Repository<ItemBase>(context);

            return rep.Where<StockItem>(o => o.Deleted == false).ToList();
        }

        /// <summary>
        /// Imports the items.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="path">The path.</param>
        public void ImportItems(IUnitOfWork context, string path)
        {
            // TODO Import items.
            throw new NotImplementedException();
        }

        /// <summary>
        /// Creates the charge department.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="chargeDepartment">The charge department.</param>
        public void CreateChargeDepartment(IUnitOfWork context, ChargeDepartment chargeDepartment)
        {
            var rep = new Repository<ChargeDepartment>(context);

            CheckDuplicate(rep, chargeDepartment);
            chargeDepartment.Deleted = false;
            rep.Add(chargeDepartment);
        }

        /// <summary>
        /// Updates the charge department.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="chargeDepartment">The charge department.</param>
        public void UpdateChargeDepartment(IUnitOfWork context, ChargeDepartment chargeDepartment)
        {
            var rep = new Repository<ChargeDepartment>(context);

            var entity = rep.GetChargeDepartment(chargeDepartment.Id);
            if (chargeDepartment.Name != entity.Name)
            {
                CheckDuplicate(rep, chargeDepartment);
                entity.Name = chargeDepartment.Name;
            }
        }

        /// <summary>
        /// Deletes the charge department.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="chargeDepartmentId">The charge department id.</param>
        /// <param name="deletedItems">The deleted items.</param>
        public void DeleteChargeDepartment(IUnitOfWork context, int chargeDepartmentId, IList<int> deletedItems)
        {
            var rep = new Repository<ChargeDepartment>(context);
            var entity = rep.GetChargeDepartment(chargeDepartmentId);
            entity.Deleted = true;
            foreach (var item in GetItemsOfChargeDepartment(context, chargeDepartmentId))
            {
                item.Deleted = true;
                deletedItems.Add(item.Id);
            }
        }

        /// <summary>
        /// Gets all charge departments.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        /// A list of all charge departments.
        /// </returns>
        public IList<ChargeDepartment> GetAllChargeDepartments(IUnitOfWork context)
        {
            var rep = new Repository<ChargeDepartment>(context);
            return rep.Where(o => o.Deleted == false).ToList();
        }

        /// <summary>
        /// Gets the item.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="id">The id.</param>
        /// <param name="includeCategory">if set to <c>true</c> 
        /// the category will be fetched.</param>
        /// <param name="includeVendor">if set to <c>true</c> 
        /// the vendor will be fetched.</param>
        /// <param name="includeChargeDepartment">if set to <c>true</c>
        /// the charge department will be fetched.</param>
        /// <returns>
        /// The item.
        /// </returns>
        public StockItem GetItem(IUnitOfWork context, int id, bool includeCategory,
            bool includeVendor, bool includeChargeDepartment)
        {
            var rep = new Repository<ItemBase>(context);
            var item = rep.GetItem(id);
            if (includeCategory)
            {
                var i = item.Category.Id;
            } if (includeVendor)
            {
                var i = item.Vendor.Id;
            }
            if (includeChargeDepartment)
            {
                var i = item.ChargeDepartment.Id;
            }
            return item;
        }

        #endregion

        #region Private Methods

        private void CheckDuplicate(Repository<ItemBase> rep, StockItem entity)
        {
            if (rep.Count(o => o.Name == entity.Name && o.Deleted == false) > 0)
            {
                throw new DuplicateException()
                {
                    Entity = "Stock Item",
                    Property = "Name",
                    Value = entity.Name,
                };
            }
        }

        private void CheckDuplicate(Repository<ChargeDepartment> rep, ChargeDepartment entity)
        {
            if (rep.Count(o => o.Name == entity.Name && o.Deleted == false) > 0)
            {
                throw new DuplicateException()
                {
                    Entity = "Charge Department",
                    Property = "Name",
                    Value = entity.Name,
                };
            }
        }

        private ChargeDepartment GetChargeDepartment(IUnitOfWork context, int id)
        {
            var rep = new Repository<ChargeDepartment>(context);
            return rep.GetChargeDepartment(id);
        }

        private IEnumerable<StockItem> GetItemsOfChargeDepartment(IUnitOfWork context, int chargeDepartmentId)
        {
            var rep = new Repository<ItemBase>(context);
            return rep.Where<StockItem>(o => o.ChargeDepartment.Id == chargeDepartmentId && o.Deleted == false);
        }

        #endregion
    }
}
